﻿using System;
using System.Windows.Forms;
using System.Diagnostics.Contracts;
using System.Collections.Generic;

namespace DotNet.Common
{
	class UndoRedoInfo
	{
		public UndoRedoInfo(string text, int cursorLoc)
		{
			Text = text;
			CursorLocation = cursorLoc;
		}
		public readonly int CursorLocation;
		public readonly string Text;
	}

	/// <summary>
	/// 文本框编辑类
	/// </summary>
	class TextEdit
	{
		private ContextMenuStrip m_TextcontextMenuStrip = new ContextMenuStrip();
		private ToolStripMenuItem m_menuUnDo = new ToolStripMenuItem();
		private ToolStripMenuItem m_menuReDo = new ToolStripMenuItem();
		private ToolStripSeparator m_menuseparator1 = new ToolStripSeparator();
		private ToolStripMenuItem m_menuCut = new ToolStripMenuItem();
		private ToolStripMenuItem m_menuCopy = new ToolStripMenuItem();
		private ToolStripMenuItem m_menuPaste = new ToolStripMenuItem();
		private ToolStripMenuItem m_menuDelete = new ToolStripMenuItem();
		private ToolStripSeparator m_menuseparator2 = new ToolStripSeparator();
		private ToolStripMenuItem m_menuSelectAll = new ToolStripMenuItem();

		private List<UndoRedoInfo> m_UndoList = new List<UndoRedoInfo>();
		private Stack<UndoRedoInfo> m_RedoStack = new Stack<UndoRedoInfo>();
		private bool m_isUserInput = true;
		private UndoRedoInfo m_LastInfo = new UndoRedoInfo(string.Empty, 0);
		private int m_MaxUndoRedoSteps = 50;

		TextBoxBase m_textBox;

		public event BoolInvoker OnUnDoEnableChanged;
		public event BoolInvoker OnReDoEnableChanged;

		public int MaxUndoRedoSteps
		{
			set { m_MaxUndoRedoSteps = value; }
			get { return m_MaxUndoRedoSteps; }
		}

		public TextEdit()
		{
			// 
			// ucfTextcontextMenuStrip
			// 
			m_TextcontextMenuStrip.Items.AddRange(new ToolStripItem[] {
				m_menuUnDo,
				m_menuReDo,
				m_menuseparator1,
				m_menuCut,
				m_menuCopy,
				m_menuPaste,
				m_menuDelete,
				m_menuseparator2,
				m_menuSelectAll});
			m_TextcontextMenuStrip.Name = "TextcontextMenuStrip";
			m_TextcontextMenuStrip.Size = new System.Drawing.Size(113, 142);
			// 
			// menuUnDo
			// 
			m_menuUnDo.Name = "menuUnDo";
			m_menuUnDo.Size = new System.Drawing.Size(112, 22);
			m_menuUnDo.Text = "撤销(&U)";
			// 
			// menuReDo
			// 
			m_menuReDo.Name = "menuReDo";
			m_menuReDo.Size = new System.Drawing.Size(112, 22);
			m_menuReDo.Text = "重做(&R)";
			// 
			// menuCut
			// 
			m_menuCut.Name = "menuCut";
			m_menuCut.Size = new System.Drawing.Size(112, 22);
			m_menuCut.Text = "剪切(&T)";
			// 
			// menuCopy
			// 
			m_menuCopy.Name = "menuCopy";
			m_menuCopy.Size = new System.Drawing.Size(112, 22);
			m_menuCopy.Text = "复制(&C)";
			// 
			// menuPaste
			// 
			m_menuPaste.Name = "menuPaste";
			m_menuPaste.Size = new System.Drawing.Size(112, 22);
			m_menuPaste.Text = "粘贴(&P)";
			// 
			// m_menuDelete
			// 
			m_menuDelete.Name = "menuDelete";
			m_menuDelete.Size = new System.Drawing.Size(112, 22);
			m_menuDelete.Text = "删除(&D)";
			// 
			// separator1
			// 
			m_menuseparator1.Name = "separator1";
			m_menuseparator1.Size = new System.Drawing.Size(109, 6);
			// 
			// separator2
			// 
			m_menuseparator2.Name = "separator2";
			m_menuseparator2.Size = new System.Drawing.Size(109, 6);
			// 
			// m_menuSelectAll
			// 
			m_menuSelectAll.Name = "menuSelectAll";
			m_menuSelectAll.Size = new System.Drawing.Size(112, 22);
			m_menuSelectAll.Text = "全选(&A)";

			m_menuUnDo.Click += new EventHandler(m_menuUnDo_Click);
			m_menuReDo.Click += new EventHandler(m_menuReDo_Click);
			m_menuCut.Click += new EventHandler(m_menuCut_Click);
			m_menuCopy.Click += new EventHandler(m_menuCopy_Click);
			m_menuPaste.Click += new EventHandler(m_menuPaste_Click);
			m_menuDelete.Click += new EventHandler(m_menuDelete_Click);
			m_menuSelectAll.Click += new EventHandler(m_menuSelectAll_Click);

			m_TextcontextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(m_TextcontextMenuStrip_Opening);
		}

		/// <summary>
		/// 加载文本框
		/// </summary>
		/// <param name="textBox"></param>
		public void Load(TextBoxBase textBox)
		{
			Contract.Requires(textBox != null);

			textBox.ContextMenuStrip = m_TextcontextMenuStrip;
			textBox.TextChanged += textBox_TextChanged;

			m_textBox = textBox;
			m_LastInfo = this.CurrentUndoRedoInfo;
		}

		void m_TextcontextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
		{
			m_menuPaste.Enabled = Clipboard.ContainsText();
			m_menuDelete.Enabled = (m_textBox.SelectionLength > 0);
		}

		void m_menuUnDo_Click(object sender, EventArgs e)
		{
			this.UnDo();
		}

		void m_menuReDo_Click(object sender, EventArgs e)
		{
			this.ReDo();
		}

		void m_menuCut_Click(object sender, EventArgs e)
		{
			this.Cut();
		}

		void m_menuCopy_Click(object sender, EventArgs e)
		{
			this.Copy();
		}

		void m_menuPaste_Click(object sender, EventArgs e)
		{
			this.Paste();
		}

		void m_menuDelete_Click(object sender, EventArgs e)
		{
			this.Delete();
		}

		void m_menuSelectAll_Click(object sender, EventArgs e)
		{
			m_textBox.SelectAll();
			m_textBox.Focus();
		}

		public void Clear()
		{
            m_textBox.Text = string.Empty;

			m_UndoList.Clear();
			m_RedoStack.Clear();

			this.UnDoEnabled = false;
			this.ReDoEnabled = false;
		}

		public bool UnDoEnabled
		{
			private set
			{
				m_menuUnDo.Enabled = value;
				if (OnUnDoEnableChanged != null)
					OnUnDoEnableChanged(value);
			}
			get
			{
				return m_UndoList.Count > 0;
			}
		}

		public bool ReDoEnabled
		{
			private set
			{
				m_menuReDo.Enabled = value;
				if (OnReDoEnableChanged != null)
					OnReDoEnableChanged(value);
			}
			get
			{
				return m_RedoStack.Count > 0;
			}
		}

		UndoRedoInfo CurrentUndoRedoInfo
		{
			get
			{
				return new UndoRedoInfo(m_textBox.Text, m_textBox.SelectionStart);
			}
			set
			{
				m_textBox.Text = value.Text;
				m_textBox.SelectionStart = value.CursorLocation;
				m_textBox.ScrollToCaret();

				m_LastInfo = value;
			}
		}

		void textBox_TextChanged(object sender, EventArgs e)
		{
			if (m_isUserInput)
			{
				if (m_menuReDo.Enabled)
				{
					m_RedoStack.Clear();
					this.ReDoEnabled = false;
				}
				AddLastInfo();
			}
		}

		void AddLastInfo()
		{
			m_UndoList.Add(m_LastInfo);
			if (m_UndoList.Count > m_MaxUndoRedoSteps)
			{
				m_UndoList.RemoveAt(0);
			}

			m_LastInfo = this.CurrentUndoRedoInfo;
			this.UnDoEnabled = true;
		}

		/// <summary>
		/// 撤销
		/// </summary>
		public void UnDo()
		{
			if (!this.UnDoEnabled)
				return;

			m_isUserInput = false;
			{
				m_RedoStack.Push(this.CurrentUndoRedoInfo);
				this.ReDoEnabled = true;

				UndoRedoInfo info = m_UndoList[m_UndoList.Count - 1];
				this.CurrentUndoRedoInfo = info;

				m_UndoList.RemoveAt(m_UndoList.Count - 1);
				this.UnDoEnabled = this.UnDoEnabled;
			}
			m_isUserInput = true;
		}

		/// <summary>
		/// 重做
		/// </summary>
		public void ReDo()
		{
			if (!this.ReDoEnabled)
				return;

			m_isUserInput = false;
			{
				AddLastInfo();

				this.CurrentUndoRedoInfo = m_RedoStack.Pop();
				this.ReDoEnabled = this.ReDoEnabled;
			}
			m_isUserInput = true;
		}

		/// <summary>
		/// 剪切
		/// </summary>
		public void Cut()
		{
			m_textBox.Cut();
		}

		/// <summary>
		/// 复制
		/// </summary>
		public void Copy()
		{
			m_textBox.Copy();
		}

		/// <summary>
		/// 粘贴
		/// </summary>
		public void Paste()
		{
			m_textBox.Paste();
		}

		/// <summary>
		/// 删除
		/// </summary>
		public void Delete()
		{
			m_textBox.SelectedText = string.Empty;
		}
	}
}
